/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering, 
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.protocol;

import cgl.narada.util.ByteUtilities;
/** 
    This class serves as a helper to the NodeAdditionProtocol, and is 
    responsible the creation of various kinds of request and response packet
    that exist within the Node Organization Protocol 
    
    @author Shrideep Pallickara
    $Revision$
    $Date$
*/
public class NodeAdditionProtocolHelper implements ProtocolDebugFlags {
  
  
  public NodeAdditionProtocolHelper() {
    

  }


  /** This method is responsible for constructing the node addition request.
      A node specifies the kind of gateways it seeks to be in the integer
      array, while specifying the <i>to/from</i> charachteristics of the 
      node addition request 
      @param levels - Integer array specifying the kind of gateways the node
      seeks to be.
      @param from - Speicification of the <i>to/from</i> relationship for
      node addition.
  */
  public byte[] constructNodeAdditionRequest(int[] levels, boolean from) {
    int connectionRequest;
    byte[] connectionRequestPacket = new byte[2];
    /** The first byte in the request packet indicates the kind of request
	it is */
    connectionRequestPacket[0] = Protocol.NAP_SETUP_REQUEST;
    if (from) {
      /** 1 signifies the "from" relationship, while 0 signifies the "to"
	  relationship */
      connectionRequest =  1;
    } else {  
      connectionRequest =  0;
    }
    for (int i =0; i < levels.length ; i++) {
      connectionRequest = connectionRequest | (1 << levels[i]) ;
    }
    System.out.println("Connection Request " + 
		       ByteUtilities.printByte((byte)connectionRequest));
    connectionRequestPacket[1] = (byte)connectionRequest;
    return connectionRequestPacket;
  }

  public byte[] constructNodeAdditionResponse(ProtocolID requestId,
					      int requestedLevel, 
					      int systemLevel,
					      NodeAddress addressAssigned,
					      NodeAddress assignedBy,
					      NodeAddress connectedTo) {
    int offset=0;
    byte[] nodeAdditionResponse = new byte[ 1 /* Protocol.NAP_RESONSE */ +
					  1 /*Code - Success|Error|Fail */ +
					  10 /* The unique request-ID */ +
					  1 /*Requested Level */ +
					  1 /*System Level */ +
					  4*(systemLevel+1) /*addr assigned*/+
					  4*(systemLevel+1) /*addr of node*/ +
					  4*(systemLevel+1) /*connected to*/];
    nodeAdditionResponse[offset++] = Protocol.NAP_SETUP_RESPONSE;
    nodeAdditionResponse[offset++] = (byte) 0;
    System.arraycopy(requestId.getBytes(), 0, 
		     nodeAdditionResponse, offset, 10);
    offset+=10;
    nodeAdditionResponse[offset++] = (byte) requestedLevel;
    nodeAdditionResponse[offset++] = (byte) systemLevel;
    System.arraycopy(addressAssigned.getAddressInBytes(), 0, 
		     nodeAdditionResponse, offset, 4*(systemLevel+1));
    offset+= (4*(systemLevel+1));
    System.arraycopy(assignedBy.getAddressInBytes(), 0, 
		     nodeAdditionResponse, offset, 4*(systemLevel+1));
    offset+= (4*(systemLevel+1));
    System.arraycopy(connectedTo.getAddressInBytes(), 0, 
		     nodeAdditionResponse, offset, 4*(systemLevel+1));
    return nodeAdditionResponse;
    
  }


  public byte[] constructNodeAdddressRequest(int requestedLevel,
					     int systemLevel, 
					     ProtocolID requestId, 
					     Destinations origin,
					     Destinations toReach,
					     Destinations traversedSoFar) {
    byte[] nodeAddressRequest = 
      new byte[1 /*identifier */ +1 /*requestedLevel*/ + 10 /*requestId*/ + 
	      3*4*(systemLevel+1) /* Origin + toReach + reachedSoFar */];
    int offset=0;
    nodeAddressRequest[offset++] = Protocol.NAP_ADDRESS_REQUEST;
    nodeAddressRequest[offset++] = (byte) requestedLevel;
    
    /** Marshalling the requestID */
    System.arraycopy(requestId.getBytes(), 0, /* The source */ 
		     nodeAddressRequest, offset, 10 /*The destination */);
    offset+=10;
    
    /** Marshalling the origins */
    System.arraycopy(origin.getDestinationsInBytes(), 0,
		     nodeAddressRequest, offset, 4*(systemLevel+1) );
    offset+=(4*(systemLevel+1));
    
    /** Marshalling the destination toReach */
    System.arraycopy(toReach.getDestinationsInBytes(), 0,
		     nodeAddressRequest, offset, 4*(systemLevel+1) );
    offset+=(4*(systemLevel+1));
    
    /** Marshall the destinations reached so far */
    System.arraycopy(traversedSoFar.getDestinationsInBytes(), 0, /*source*/ 
		     nodeAddressRequest, offset /*The destination*/,
		     4*(systemLevel+1));
    return nodeAddressRequest;
  }

  
  public byte[] constructNodeAddressResponse(int requestedLevel,
					     int systemLevel, 
					     ProtocolID requestId, 
					     NodeAddress addressAssigned,
					     NodeAddress assignedBy,
					     Destinations toReach,
					     Destinations traversedSoFar) {
    byte[] nodeAddressResponse = 
      new byte [1 /* The header */ +  1  /*The requested Level */ +
	       10 /** The requestId */ +
	       4*4*(systemLevel+1) /*The address assigned, assignedby,
				     destination to
				     reach and the hops traveresed so far */];
    int offset=0;
    nodeAddressResponse[offset++] = Protocol.NAP_ADDRESS_RESPONSE;
    nodeAddressResponse[offset++] = (byte) requestedLevel;
    
    System.arraycopy(requestId.getBytes(), 0, 
		     nodeAddressResponse, offset, 10);
    offset+=10;
    
    System.arraycopy(addressAssigned.getAddressInBytes(), 0, 
		     nodeAddressResponse, offset, 4*(systemLevel+1));
    offset+= (4*(systemLevel+1));

    System.arraycopy(assignedBy.getAddressInBytes(), 0, 
		     nodeAddressResponse, offset, 4*(systemLevel+1));
    offset+= (4*(systemLevel+1));
    
    System.arraycopy(toReach.getDestinationsInBytes(), 0,
		     nodeAddressResponse, offset, 4*(systemLevel+1));
    offset+= (4*(systemLevel+1));
    
    System.arraycopy(traversedSoFar.getDestinationsInBytes(), 0,
		     nodeAddressResponse, offset, 4*(systemLevel+1));
    
    return nodeAddressResponse;
  }

  /** This method simply updates the routing information contained within an 
      event to indicate that the event was routed to this node too. The 
      process is a simple one with a logical biut-wise OR of the node address
      and the received routing information*/
  private byte[] updateRoutingInfoDownTillLevel(byte[] receivedRoutingInfo, 
						byte[] addressBytes,
						int level) {
    /* This determines how much routing information has been received over
       the link. The minimum routing info is of length=1, which corresponds 
       to the lowest order node. */
    byte[] updatedRoutingInfo = new byte[level*4];
    
    /** This copying ensures that even though the routing info received
	is that of the lowest level, the other levels (upwards till 
	<i>level</i> are included within the routing information */
    System.arraycopy(addressBytes, 0, updatedRoutingInfo, 0, 16);
    
    for (int i=0; i < level; i++) {
      for (int j=0; j <4; j++) 
	updatedRoutingInfo[4*i +j] = 
	  logicalOrBytes(receivedRoutingInfo[4*i+j],
			 updatedRoutingInfo[4*i +j]);
    }
    
    return updatedRoutingInfo;
  }
  
  
  /** Returns the result of a logical OR between 2 bytes as a byte */
  private byte logicalOrBytes(byte a, byte b) {
    int x = a;
    int y = b;
    return (byte) (x|y);
  }

  /** When an address has been created it needs to be routed back to the node
      where this node was created. 
  */
  public void routeNodeAddressResponse(NodeAddress assignedAddress, int level, 
				       NodeAddress toAddress ) {

  }

}


